/****
 *
 * Non-instantiable utility class LexicalAnalyzer,
 * used for generating machine code.
 * @author Jason Welch, Codi Gann, Jamal Martin
 *  
 ****/
package micropascalcomp;

import java.io.*;
import java.util.LinkedList;

public class LexicalAnalyzer {

    private static int label = 1;   //static var for label numbers

    // Suppress default constructor for noninstantiability
    private LexicalAnalyzer() {
        throw new AssertionError();
    }//end statement

    //Method that gives you the current label value and increases it
    public static int getLabel() {
        return label++;
    }//end statement

    //Method that gens a MM Negative stack if a option sign is found
    public static void genNeg() {
        output("NEGS", false);
    }//end statement

    //method that generates a push # in MM if a unsigned int is found
    public static void genUnsignedInt(int i) {
        output("PUSH #" + i, false);
    }//end statement

    public static void genVariable(String lexeme, LinkedList<SymbolTable> symbols) {
        int index = -1;
        for (int i = 0; i < symbols.size(); i++) {
            index = symbols.get(i).has(lexeme);
        }
        if (index > -1) {
            output("PUSH " + (index + 1) + "(D0)", false);
        } else {
            System.out.println("Attempted to use variable: " + lexeme + " before defining it.");
        }
    }//end statement

    public static void genAssignment(String lexeme, LinkedList<SymbolTable> symbols) {
        int index = -1;
        for (int i = 0; i < symbols.size(); i++) {
            index = symbols.get(i).has(lexeme);
        }
        if (index > -1) {
            output("POP " + (index + 1) + "(D0)", false);
        } else {
            System.out.println("Attempted to use variable: " + lexeme + " before defining it.");
        }
    }//end statement

    public static void genAddition(String op) {
        if (op.compareTo("plus") == 0) {
            output("ADDS", false);
        } else if (op.compareTo("minus") == 0) {
            output("SUBS", false);
        } else if (op.compareTo("or") == 0) {
            int l1 = getLabel();
            int l2 = getLabel();
            int l3 = getLabel();
            output("BEQ -1(SP) #1 L" + l1, false);
            output("SUB SP #1 SP", false);
            output("BEQ -1(SP) #1 L" + l2, false);
            output("SUB SP #1 SP\nPUSH #0", false);
            output("BR L" + l3 + "\nL" + l1 + ":", false);
            output("SUB SP #2 SP\nPUSH #1", false);
            output("BR L" + l3 + "\nL" + l2 + ":", false);
            output("SUB SP #1 SP\nPUSH #1", false);
            output("L" + l3 + ":", false);
        }
    }//end statement

    public static void genMultiplication(String op) {
        if (op.compareTo("times") == 0) {
            output("MULS", false);
        } else if (op.compareTo("div") == 0) {
            output("DIVS", false);
        } else if (op.compareTo("mod") == 0) {
            output("PUSH -2(SP)\nPUSH -2(SP)", false);
            output("DIVS\nMULS\nSUBS", false);
        } else if (op.compareTo("and") == 0) {
            int l1 = getLabel();
            int l2 = getLabel();
            int l3 = getLabel();
            output("BEQ -1(SP) #1 L" + l1, false);
            output("SUB SP #2 SP\nPUSH #0", false);
            output("BR L" + l3 + "\nL" + l1 + ":", false);
            output("SUB SP #1 SP", false);
            output("BEQ -1(SP) #1 L" + l2, false);
            output("SUB SP #1 SP\nPUSH #0", false);
            output("BR L" + l3 + "\nL" + l2 + ":", false);
            output("SUB SP #1 SP\nPUSH #1", false);
            output("L" + l3 + ":", false);
        }
    }//end statement

    /**
     * Function that generates the MM code of a relational expresion
     * @param op The value of the opperand in string form
     */
    public static void genRelation(String op, int l1, int l2) {
        if (op.compareTo("eql") == 0) {
            output("BEQ -2(SP) -1(SP) L" + l1, false);
            output("SUB SP #2 SP\nPUSH #0", false);
            output("BR L" + l2 + "\nL" + l1 + ":", false);
            output("SUB SP #2 SP\nPUSH #1", false);
            output("L" + l2 + ":", false);
        } else if (op.compareTo("lt") == 0) {
            output("BLT -2(SP) -1(SP) L" + l1, false);
            output("SUB SP #2 SP\nPUSH #0", false);
            output("BR L" + l2 + "\nL" + l1 + ":", false);
            output("SUB SP #2 SP\nPUSH #1", false);
            output("L" + l2 + ":", false);
        } else if (op.compareTo("gt") == 0) {
            output("BGT -2(SP) -1(SP) L" + l1, false);
            output("SUB SP #2 SP\nPUSH #0", false);
            output("BR L" + l2 + "\nL" + l1 + ":", false);
            output("SUB SP #2 SP\nPUSH #1", false);
            output("L" + l2 + ":", false);
        } else if (op.compareTo("lte") == 0) {
            output("BLE -2(SP) -1(SP) L" + l1, false);
            output("SUB SP #2 SP\nPUSH #0", false);
            output("BR L" + l2 + "\nL" + l1 + ":", false);
            output("SUB SP #2 SP\nPUSH #1", false);
            output("L" + l2 + ":", false);
        } else if (op.compareTo("gte") == 0) {
            output("BGE -2(SP) -1(SP) L" + l1, false);
            output("SUB SP #2 SP\nPUSH #0", false);
            output("BR L" + l2 + "\nL" + l1 + ":", false);
            output("SUB SP #2 SP\nPUSH #1", false);
            output("L" + l2 + ":", false);
        } else if (op.compareTo("neql") == 0) {
            output("BNE -2(SP) -1(SP) L" + l1, false);
            output("SUB SP #2 SP\nPUSH #0", false);
            output("BR L" + l2 + "\nL" + l1 + ":", false);
            output("SUB SP #2 SP\nPUSH #1", false);
            output("L" + l2 + ":", false);
        }
    }//end statement
    
    public static void genNot(int l1, int l2){
        output("BEQ -1(SP) #0 L" + l1, false);
        output("SUB SP #1 SP", false);
        output("PUSH #0\nBR L" + l2, false);
        output("L" + l1 + ":", false);
        output("SUB SP #1 SP", false);
        output("PUSH #1\nL" + l2 + ":", false);
    }

    public static void genThenElse(int i, int l1, int l2) {
        switch (i) {
            case 1:
                output("BEQ -1(SP) #0 L" + l1, false);
                output("SUB SP #1 SP", false);
                break;
            case 2:
                output("BR L" + l2, false);
                output("L" + l1 + ":", false);
                output("SUB SP #1 SP", false);
                break;
            case 3:
                output("L" + l2 + ":", false);
                break;
            case 4:
                output("BR L" + l2, false);
                output("L" + l1 + ":", false);
                output("SUB SP #1 SP", false);
                output("L" + l2 + ":", false);
            default:
                break;
        }
    }//end statement

    public static void genFor(String id,String step, int i, int l1, int l2,
            int l3, int l4,  LinkedList<SymbolTable> symbols) {
        switch (i) {
            case 1:
                output("L" + l1 + ":", false);
                break;
            case 2:               
                if (step.compareTo("to") == 0) {
                    output("BGE -2(SP) -1(SP) L" + l2, false);
                    output("SUB SP #1 SP", false);
                } else {
                    output("BLE -2(SP) -1(SP) L" + l2, false);
                    output("SUB SP #1 SP", false);
                }
                output("PUSH #0\nBR L" + l3, false);
                output("L" + l2 + ":\nSUB SP #1 SP\nPUSH #1", false);
                output("L" + l3 + ":", false);
                output("BEQ -1(SP) #0 L" + l4, false);
                output("SUB SP #1 SP", false);
                break;
            case 3:
                if (step.compareTo("to") == 0) {
                    genVariable(id, symbols);
                    output("PUSH #1\nADDS", false);
                    genAssignment(id, symbols);
                } else {
                    genVariable(id, symbols);
                    output("PUSH #1\nSUBS", false);
                    genAssignment(id, symbols);
                }
                output("BR L" + l1, false);
                output("L" + l4 + ":", false);
                output("SUB SP #2 SP", false);
                break;
            default:
                break;
        }
    }//end statement

    public static void genWhileHeader(int l1) {
        output("L" + l1 + ":", false);
    }//end statement

    public static void genWhile(int i, int l1, int l2) {
        switch (i) {
            case 1:
                output("BEQ -1(SP) #0 L" + l2, false);
                output("SUB SP #1 SP", false);
                break;
            case 2:
                output("BR L" + l1, false);
                output("L" + l2 + ":", false);
                break;
            default:
                break;
        }
    }//end statement
    
    public static void genRepeat(int l1, int l2) {
        output("BEQ -1(SP) #1 L" + l2, false);
        output("SUB SP #1 SP", false);
        output("BR L" + l1, false);
        output("L" + l2 + ":", false);
    }//end statement

    /**
     * Method that creates the read statement in MM
     * @param n The index size of the program symbol table
     */
    public static void genReadStatement(int n) {
        output("RD " + n + "(D0)", false);
    }//end statement

    /**
     * Method that creates the write statement in MM
     * @param n The index size of the program symbol table
     */
    public static void genWriteStatement() {
        output("WRTS", false);
    }//end statement

    /**
     * Method that creates the program heading in MM
     * @param n The index size of the program symbol table
     */
    public static void genHeading(int n) {
        output("PUSH D0", false);
        output("MOV SP D0", false);
        output("ADD SP #" + n + " SP", false);
    }//end statement

    /**
     * Method that creates the program ending in MM
     * @param n The index size of the program symbol table
     */
    public static void genEnding(int n) {
        output("SUB SP #" + n + " SP", false);
        output("POP D0", false);
        output("HLT", true);
    }//end statement
    /***
     * There is file i/o stuff down here
     * you must use setFileName before you
     * use any other function.
     ***/
    private static FileWriter fstream = null;
    private static BufferedWriter out = null;
    private static String fName = null;

    public static void setFileName(String name) {
        if (fName == null) {
            fName = name;
        }
    }

    private static void output(String value, boolean end) {
        if (fName != null) {
            try {
                if (fstream == null) {
                    fstream = new FileWriter(fName);
                    out = new BufferedWriter(fstream);
                }//end if

                //System.out.println(value);
                out.write(value + "\n");

                if (out != null && end == true) {
                    out.close();
                    System.out.println("File closed");
                }

            } catch (IOException e) {
                e.printStackTrace();
            }//end try
        } else {
            System.err.println("Use LexicalAnalyzer.setFileName(String).");
        }
    }//end output
}//end class

